package utils

import (
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

var projPath string

// ExePath 返回当前执行文件所在的路径
func ExePath() string {
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	return dir
}

// ProjectPath 返回当前的go程序的工程路径 也就是其所属的 gopath
func ProjectPath() string {
	// 若已经检查过工程路径，则使用缓存的结果
	if projPath != `` {
		return projPath
	}

	// 检查是否有 GOWORK 环境变量设置，如果有直接使用此环境变量设置的路径
	gowork := os.Getenv(`GOWORK`)
	if gowork != `` {
		path, _ := filepath.Abs(filepath.Dir(gowork))
		projPath = path
		return path
	}

	// 检查是否有 GOMOD 环境变量设置，如果有直接使用此环境变量设置的路径
	gomod := os.Getenv(`GOMOD`)
	if gomod != `` {
		path, _ := filepath.Abs(filepath.Dir(gomod))
		projPath = path
		return path
	}

	// 待检查的路径列表，先检查当前路径，再检查执行文件所在路径
	// 若两个路径都不符合 Go 工程的特征，则使用执行文件所在路径作为工程路径
	pendingChecks := []string{}
	wd, err := os.Getwd()
	if err == nil {
		pendingChecks = append(pendingChecks, wd)
	}
	pendingChecks = append(pendingChecks, filepath.Dir(os.Args[0]))

	dir := ``
	// 遍历待检查路径列表
	for _, wd := range pendingChecks {
		// 获得绝对路径
		dir, err = filepath.Abs(wd)
		if err != nil {
			continue
		}
		if runtime.GOOS == `windows` {
			dir = strings.ToLower(dir)
		}

		// 检查指定路径是否是 GOPATH 环境变量(中的某一个路径)的子目录
		// 如果是则返回 GOPATH 中的路径作为工程路径
		pathes := filepath.SplitList(os.Getenv("GOPATH"))
		for _, path := range pathes {
			path = filepath.Clean(path)
			if runtime.GOOS == `windows` {
				path = strings.ToLower(path)
			}
			if strings.HasPrefix(dir, path) {
				projPath = path
				return path
			}
		}

		gomod := ``
		parentDir := dir
		// 从指定路经开始，向上一级一级检查
		for parentDir != `` {
			// 如果找到 go.work 文件，表示这是 go workspace 目录，直接使用此路径作为工程路径
			if IsFileOrDirectoryExist(parentDir + `/go.work`) {
				projPath = parentDir
				return parentDir
			}
			// 如果找到 go.mod 文件，表示这是 go module 目录，此路径作为备选
			// 若所有的父级目录中都不是 go workspace，则以最上层的 go module 目录作为工程路径
			if IsFileOrDirectoryExist(parentDir + `/go.mod`) {
				gomod = parentDir
			}
			// 如果已经追溯至根目录则停止查找
			newDir := filepath.Dir(parentDir)
			if newDir == parentDir {
				break
			}
			parentDir = newDir
		}
		if gomod != `` {
			projPath = gomod
			return gomod
		}
	}

	// 没有找到符合 Go 工程特征的路径，使用执行文件所在路径(待检查列表中最后一个)作为工程路径
	dir = filepath.ToSlash(dir)
	projPath = dir
	return dir
}

// CreatePath 创建目录,可创建多级目录
func CreatePath(dir string) error {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return err
	}
	return os.MkdirAll(dir, 0755)
}

// IsFileOrDirectoryExist 判断文件或者目录是否存在
func IsFileOrDirectoryExist(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		return false
	}
	return true
}
